package at.fhooe.tuxgestures.cam.data;

import ij.plugin.Raw;
import ij.process.FloatProcessor;

import java.awt.Image;
import java.awt.image.RenderedImage;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

import at.fhooe.tof.fileread.TOFStreaming;
import at.fhooe.tuxgestures.cam.ICameraController;
import at.fhooe.tuxgestures.cam.data.event.DataEvent;
import at.fhooe.tuxgestures.cam.data.event.IDataEventListener;
import at.fhooe.tuxgestures.cam.resources.Resources;

public class CameraDataProvider implements ICameraDataProvider, Runnable
{
	private List<IDataEventListener> listeners = new ArrayList<IDataEventListener>();

	private File tofFile = null;

	private Thread readingThread = null;
	private boolean readFromCam = true;
	
//	private boolean SAVE_IMAGES = false;
	
	/**
	 * Defines if the camera reads forever or only for a
	 * certain number of loop cycles (for testing...).
	 * If set to zero (0) or below it reads forever.
	 */
	private int CAMERA_READ_LIMIT = Resources.getCalibrationIntValue("CAMERA_READ_LIMIT");
	
	/**
	 * Defines how long the reading thread sleeps after it
	 * read one frame and sent it...
	 */
	private static final int READ_INTERVAL = Resources.getCalibrationIntValue("READ_INTERVAL");

	private void fireDataEvent(DataEvent event)
	{
		for (IDataEventListener listener : listeners)
			listener.newData(event);
	}

	@Override
	public void startReadFromFile(File tofFile)
	{
		this.tofFile = tofFile;
		startReadFromCamera();
	}

	@Override
	public void startReadFromCamera()
	{
		readingThread = new Thread(this);
		readingThread.run();
	}

	@Override
	public void addListener(IDataEventListener listener)
	{
		if (!listeners.contains(listener))
			listeners.add(listener);

	}

	@Override
	public void removeListener(IDataEventListener listener)
	{
		listeners.remove(listener);
	}

	/**
	 * Read data in packages from a file and send {@link DataEvent}s to the
	 * {@link ICameraController}.
	 */
	private void readDataFromFile()
	{
		if (tofFile != null && tofFile.exists())
		{
			// float[][] bla = new float[][] {{1, 2}, {3, 4}};
			// fireDataEvent(new DataEvent(this, bla));

			byte[] result = new byte[(int) tofFile.length()];
			try
			{
				DataInputStream input = null;
				try
				{
					int totalBytesRead = 0;
					input = new DataInputStream(new FileInputStream(tofFile));
					int frameCount = 0;
					while (totalBytesRead < result.length)
					{
						input.skip(128);

						float[][] frame = new float[160][120];

						// faster approach
						byte[] buffer = new byte[160 * 120 * 4];
						input.read(buffer);
						ByteBuffer temp = ByteBuffer.wrap(buffer);
						temp.order(ByteOrder.LITTLE_ENDIAN);
						FloatBuffer fbuf = temp.asFloatBuffer();

						float[] pixels = new float[fbuf.limit()];
						fbuf.get(pixels);
//						if (SAVE_IMAGES)
//						{
//							FloatProcessor fp = new FloatProcessor(160, 120,
//									pixels);
//							
//							fp = new FloatProcessor(convert2D(pixels, 160, 120));
//							Image img = fp.createImage();
//
//							File outputfile = new File(String.format(
//									"Frames/jframe%d.png", frameCount));
//							ImageIO.write((RenderedImage) img, "png",
//									outputfile);
//						}

						frameCount++;
						fireDataEvent(new DataEvent(this, pixels));
						totalBytesRead += 19328;
					}
				} finally
				{
					input.close();
				}
			} catch (Exception ex)
			{
				ex.printStackTrace();
			}
		} else
			System.err.println("File not found!");
	}

	private void readFromCamera()
	{
		TOFStreaming tofLive = null;
		try {
			File libPMDaccess = new File("libs/pmdaccess2.dll");
			File libTOFStreaming = new File("libs/TOFStreaming.dll");
			System.load(libPMDaccess.getAbsolutePath());
			System.load(libTOFStreaming.getAbsolutePath());
			
			//********* live stream ***********//
			tofLive = new TOFStreaming();
			tofLive.initStream();
			float[] fArray = null;
			int frameCounter = 0;
			while(true)
			{
				fArray = tofLive.getStream();
				if (fArray != null && fArray.length > 0)
				{
					fireDataEvent(new DataEvent(this, fArray));
				}
				else
				{
					System.err.println("Could not read data! Is the camera connected?");
				}
				
//				System.out.println(frameCounter);
				
				if (CAMERA_READ_LIMIT > 0 && frameCounter > CAMERA_READ_LIMIT)
					break;
				frameCounter++;
				
				// sleep for some milliseconds, we don't have to 
				// read every frame...
				Thread.sleep(READ_INTERVAL);
			}
		} catch (Exception e) {
			tofLive.close();
			e.printStackTrace();
		}
		finally
		{
			tofLive.close();
		}
	}
	
	@Override
	public void run()
	{
		if (tofFile != null)
		{
			// read from file
			readDataFromFile();
		} else
		{
			readFromCamera();
		}
	}

	@Override
	public void stopReading()
	{
		try
		{
			readFromCam = false;
			readingThread.wait();
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}

}
